package com.jfinal.plugin.activerecord.sql;

import com.jfinal.kit.StrKit;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * SQL条件-构造器
 * <p>
 * Cnd.create("id =? ", 100).and("name", "=", "xxx").or("name", "like", "aaa")
 * <p>
 *
 * @author Chen
 * @date 15/5/26
 */
public class Cnd {

    public static final String AND = " AND ";
    public static final String OR = " OR ";

    public static final String LIKE = "LIKE";
    public static final String EQ = "=";

    public static final String __LIKE = "LIKE '%xxx'";
    public static final String LIKE__ = "LIKE 'xxx%'";
    public static final String __LIKE__ = "LIKE '%xxx%'";


    protected String sqlCnd = "";

    protected List<Object> params = new ArrayList<>();

    public static Cnd create(String column, String op, Object value) {
        return new Cnd().append(AND, column, op, value);
    }

    public static Cnd create(String sqlCnd, Object... params) {
        return new Cnd().append(sqlCnd).addParams(params);
    }

    private Cnd append(String sqlCnd) {
        this.sqlCnd += sqlCnd;
        return this;
    }

    private Cnd addParam(Object param) {
        this.params.add(param);
        return this;
    }

    private Cnd addParams(Object[] params) {
        if (params != null) {
            this.params.addAll(Arrays.asList(params));
        }
        return this;
    }

    private String getAndOrStr(String andOr) {
        return StrKit.isBlank(sqlCnd) ? "" : andOr;
    }

    private Cnd append(String andOr, String column, String op, Object value) {
        //ignore null
        if (value == null || StrKit.isBlank(value.toString())) {
            return this;
        }


        if (op.contains(LIKE)) {

            switch (op) {
                case __LIKE:
                    value = "%" + StrKit.escapeSql(value.toString());
                    break;
                case LIKE__:
                    value = StrKit.escapeSql(value.toString()) + "%";
                    break;
                case __LIKE__:
                    value = "%" + StrKit.escapeSql(value.toString()) + "%";
                    break;
                default:
                    break;
            }

            op = LIKE;
        }

        String sql = getAndOrStr(andOr) + column + " " + op + " ?";
        return this.append(sql).addParam(value);
    }

    public Cnd and(Cnd cnd) {
        String sql = getAndOrStr(AND) + "(" + cnd.toSql() + ")";
        return this.append(sql).addParams(cnd.getParams());
    }

    public Cnd or(Cnd cnd) {
        String sql = getAndOrStr(OR) + "(" + cnd.toSql() + ")";
        return this.append(sql).addParams(cnd.getParams());
    }

    public Cnd and(String sqlWhere, Object... params) {
        String sql = getAndOrStr(AND) + sqlWhere;
        return this.append(sql).addParams(params);
    }

    public Cnd or(String sqlWhere, Object... params) {
        String sql = getAndOrStr(OR) + sqlWhere;
        return this.append(sql).addParams(params);
    }

    public Cnd andEx(String column, String op, Object value) {
        if (value == null) {
            return this;
        }
        return andEx(column, op, value);
    }

    public Cnd and(String column, String op, Object value) {
        return append(AND, column, op, value);
    }

    public Cnd or(String column, String op, Object value) {
        return append(OR, column, op, value);
    }

    public Cnd orEx(String column, String op, Object value) {
        if (value == null) {
            return this;
        }
        return or(column, op, value);
    }

    public String getSqlCnd() {
        return sqlCnd;
    }

    public void setSqlCnd(String sqlCnd) {
        this.sqlCnd = sqlCnd;
    }

    public String toSql() {
        return sqlCnd;
    }

    @Override
    public String toString() {
        return this.getClass().getName() + " : " + toSql();
    }

    public Object[] getParams() {
        return params.toArray();
    }


    public static void main(String[] args) {
        Cnd cnd1 = Cnd.create("id", Cnd.EQ, 100)
                .and("name", EQ, "xxx")
                .or("name", Cnd.__LIKE, "aaa")
                .or("name", Cnd.__LIKE__, "bbb");
        Cnd cnd2 = Cnd.create("id", Cnd.EQ, 200).and("name", EQ, "yyy");
        Cnd cnd3 = Cnd.create("id=?", 200).and("name", EQ, "yyy");
        Cnd cnd = cnd1.and(cnd2).or(cnd3);
        System.out.println(cnd.toSql());

        Cnd cnd4 = new Cnd().and("id=?", 200)
                .and("name", EQ, "yyy")
                .and("name", LIKE__, "yyy'");
        System.out.println("cnd4: " + cnd4.toSql());
    }

}
